1 Back to the base - plot

We’re going to use the DNase dataset, included in R’s distribution

You might want to read up a little on it, so type help(DNase) to do so - yes, nice datasets SHOULD be documented

head(DNase)
#   Run       conc density
# 1   1 0.04882812   0.017
# 2   1 0.04882812   0.018
# 3   1 0.19531250   0.121
# 4   1 0.19531250   0.124
# 5   1 0.39062500   0.206
# 6   1 0.39062500   0.215
plot(DNase$conc, DNase$density)


2 Back to the base - plot

Customizing this a little…

E.g. color the dots in blue, and add vertical lines for each value of concentration

plot(DNase$conc, DNase$density,
  ylab = attr(DNase, "labels")$y,
  xlab = paste(attr(DNase, "labels")$x, attr(DNase, "units")$x),
  pch = 3,
  col = "blue")
abline(v = unique(DNase$conc), lty = "dotted")


3 Back to the base - plot

Color the points by their value in the Run column

plot(DNase$con, DNase$density, col = DNase$Run)


4 Back to the base - plot

What if you need to check the distribution of a variable?

What if you wanted a boxplot?

Can you check out how to split the boxplots by their Run?

par(mfrow = c(1, 2))
hist(DNase$density) 
boxplot(density ~ Run, DNase)


5 What’s “wrong” with this?

While it is effective to quickly produce out of the box figures…

  • there is not global overview and parameterization of the visualization
  • the layout decisions have to be made up upfront
  • every aspect of the figure is customised locally as function arguments (one could argue that this is a plus)
  • there is no unified type of data across all functions which makes it efficient for some types of data (if they match), but also very heterogeneous in terms of interface - base graphics functions will work with various inputs: a data.frame, vectors, a formula,…
  • defaults (colours in particular!) are poorly chosen

We’ll soon use a visualization framework called the grammar of graphics (as in ggplot2) - enabling step by step construction of high quality graphics in a logical and elegant manner.

But first: hi-dim data time!


6 Hiiragi2013

A gene expression microarray dataset that reports the transcriptomes of around 100 individual cells from mouse embryos at different time points in early development

library("Hiiragi2013")
data("x")
dim(Biobase::exprs(x))
# [1] 45101   101

head(pData(x), n = 2)
#         File.name Embryonic.day Total.number.of.cells lineage genotype
# 1 E3.25  1_C32_IN         E3.25                    32               WT
# 2 E3.25  2_C32_IN         E3.25                    32               WT
#           ScanDate sampleGroup sampleColour
# 1 E3.25 2011-03-16       E3.25      #CAB2D6
# 2 E3.25 2011-03-16       E3.25      #CAB2D6

We’ll do some wrangling on that soon, so let’s create

dftx <- data.frame(t(Biobase::exprs(x)), pData(x))

7 ggplot2: the grammar of graphics

The components of ggplot2’s grammar of graphics are

  1. one or more datasets,
  2. one or more geometric objects that serve as the visual representations of the data, – for instance, points, lines, rectangles, contours,
  3. descriptions of how the variables in the data are mapped to visual properties (aesthetics) of the geometric objects, and an associated scale (e. g., linear, logarithmic, rank),
  4. one or more coordinate systems,
  5. statistical summarization rules,
  6. a facet specification, i.e. the use of multiple similar subplots to look at subsets of the same data,
  7. optional parameters that affect the layout and rendering, such text size, font and alignment, legend positions.

Simple form:

ggplot(data = <DATA>, mapping = aes(<MAPPINGS>)) +  <GEOM_FUNCTION>()

8 Advantages of the ggplot2 framework

  • The ease of getting a good looking plot
  • Easy customization
  • A lot of necessary data processing is done for you
  • Clear syntax
  • Easy multidimensional approach
  • Decent default color scheme as a default
  • Lots of extensions

9 Re-doing the DNase plot…

Exercise: create a similar plot of DNase density vs concentration, like we just did for base graphics.

library("ggplot2")
ggplot()

ggplot(data = DNase)

ggplot(data = DNase,
       mapping = aes(x = conc, y = density))


ggplot(data = DNase,
       mapping = aes(x = conc, y = density)) + 
  geom_point()

p <- ggplot(data = DNase,
            mapping = aes(x = conc, y = density)) + 
  geom_point()
p

p + geom_point(aes(color = Run))

p + geom_point(aes(color = as.character(Run)))

# ggsave("DNAse-histogram-demo.pdf", plot = p) # check out the docs for this!

Which one is your favorite? Why?


10 Exploring Hiiragi2013

What are we doing here?

ggplot(dftx, aes(x = X1426642_at, y = X1418765_at)) +
  geom_point(shape = 1) +
  geom_smooth(method = "loess")

and here?

ggplot(dftx, aes(x = X1426642_at, y = X1418765_at))  +
  geom_point(aes(color = sampleColour), shape = 19) +
  geom_smooth(method = "loess") +
  scale_color_discrete()

11 Visualizing 1D data

A common task in biological data analysis is the comparison between several samples of univariate measurements. In this section we’ll explore some possibilities for visualizing and comparing such samples. As an example, we’ll use the intensities of a set of four genes: Fgf4, Gata4, Gata6 and Sox2

selectedProbes = c(Fgf4 = "1420085_at", Gata4 = "1418863_at",
                   Gata6 = "1425463_at", Sox2 = "1416967_at")
library("dplyr")
library("tidyr")
tmp <- data.frame(t(exprs(x[selectedProbes, ])))
names(tmp) <- names(selectedProbes)
tmp$sample <- rownames(tmp)
head(tmp)
#              Fgf4    Gata4    Gata6     Sox2  sample
# 1 E3.25  3.027715 4.843137 5.500618 1.731217 1 E3.25
# 2 E3.25  9.293016 5.530016 6.160900 9.697038 2 E3.25
# 3 E3.25  2.940142 4.418059 4.584961 4.161240 3 E3.25
# 4 E3.25  9.715243 5.982314 4.753439 9.540123 4 E3.25
# 5 E3.25  8.924228 4.923580 4.629728 8.705340 5 E3.25
# 6 E3.25 11.325952 4.068520 4.165692 8.696228 6 E3.25
genes <- gather(tmp, key = "gene", value = "expression", -sample)
head(genes)
#    sample gene expression
# 1 1 E3.25 Fgf4   3.027715
# 2 2 E3.25 Fgf4   9.293016
# 3 3 E3.25 Fgf4   2.940142
# 4 4 E3.25 Fgf4   9.715243
# 5 5 E3.25 Fgf4   8.924228
# 6 6 E3.25 Fgf4  11.325952

This genes data.frame is in the so-called tidy format!
ggplot2 LOVES tidy data


12 1D: barplots, boxplots, dot, jitter, violins

ggplot(genes, aes(x = gene, y = expression)) +
  stat_summary(fun = mean, geom = "bar")

Out of the following representations, let’s discuss which one you prefer most/least

p <- ggplot(genes, aes( x = gene, y = expression, fill = gene))
p + geom_boxplot()

Try now some more options on this object p. Try to add jittered points, or use a violin plot

p + geom_jitter(aes(colour = gene))

p + geom_violin()

p + geom_dotplot(binaxis = "y", binwidth = 1/6,
       stackdir = "center", stackratio = 0.75,
       aes(color = gene))

library("ggbeeswarm")
p + geom_beeswarm(aes(color = gene))

library("ggforce")
p + geom_sina(aes(color = gene))

You can even stack multiple geom_s on another!

13 1D: densities, histograms

genes %>%
  filter(gene == "Gata4") %>%
  ggplot(aes(x = expression)) + geom_histogram()

ggplot(genes, aes(x = expression, color = gene)) + 
  geom_density()


ggplot(genes, aes(x = expression, color = gene)) + 
  geom_density() + 
  theme_bw()

There are soooo many themes available - I like clean ones, but often it depends on your purpose!


14 Visualizing 2D data

dfx <- as.data.frame(Biobase::exprs(x))
scp <- ggplot(dfx, aes(x= `59 E4.5 (PE)`, 
                       y = `92 E4.5 (FGF4-KO)`))
scp + geom_point()

Can you think of a way to reduce the overplotting here?

scp + geom_point(alpha = 0.3)

scp + geom_density2d(h = 0.5, bins = 60)

scp + geom_hex() + coord_fixed()


15 Visualizing data along more dimensions

When visualising data along additional dimension, we can parameterize the points by setting their shape, colour, size and transparency, that can be set with point aesthetics such as fill, color (or colour), shape, size and alpha.

A very powerful way to represent data along additional dimensions is facetting, i.e. producing sub-plots for different subsets of the data. Below, we first re-annotate the data using some regular expressions

ggplot(dftx, aes(x = X1426642_at, y = X1418765_at, colour = lineage)) +
  geom_point()

ggplot(dftx, aes(x = X1426642_at, y = X1418765_at)) +
  geom_point() +
  facet_grid( . ~ lineage )

ggplot(dftx,
       aes(x = X1426642_at, y = X1418765_at)) +
  geom_point() +
  facet_grid( Embryonic.day ~ lineage )

Your turn: Use facets to visualise the distribution of the four Fgf4, Gata4, Gata6 and Sox2 genes in the genes data using histograms.

ggplot(genes, aes(x = expression)) +
  geom_histogram() +
  facet_wrap(~ gene)


16 Interactive visualizations

p2 <- p + geom_jitter(aes(colour = gene))
library("plotly")
ggplotly(p2)

Sometimes this is all you might need!


17 An appetizer for RNA-seq?

…or many other high-dimensional data

  • MA plot
  • volcano plot
  • heatmaps
  • PCA plot
  • tSNE plot
  • other genomic data (karyoplots, …)

What do these plots do? Let’s discuss together.


18 An appetizer for RNA-seq?

library("pheatmap")
library("dplyr")
groups <- group_by(pData(x), sampleGroup) %>%
  summarise(n = n(), color = unique(sampleColour))
groupColor <- setNames(groups$color, groups$sampleGroup)
topGenes <- order(rowVars(Biobase::exprs(x)), decreasing = TRUE)[1:500]
rowCenter <- function(x) { x - rowMeans(x) }
pheatmap( rowCenter(Biobase::exprs(x)[ topGenes, ] ),
  show_rownames = FALSE, show_colnames = FALSE,
  breaks = seq(-5, +5, length = 101),
  annotation_col =
    pData(x)[, c("sampleGroup", "genotype", "Embryonic.day", "ScanDate") ],
  annotation_colors = list(
    sampleGroup = groupColor,
    genotype = c(`FGF4-KO` = "chocolate1", `WT` = "azure2"),
    Embryonic.day = setNames(brewer.pal(9, "Blues")[c(3, 6, 9)],
                             c("E3.25", "E3.5", "E4.5")),
    ScanDate = setNames(brewer.pal(nlevels(x$ScanDate), "YlGn"),
                        levels(x$ScanDate))
  ),
  cutree_rows = 4
)


19 Got a nice viz?

Let’s try to dissect that


20 A checklist

  1. Appropriate plot type for results - Might be a boxplot, a scatterplot, a linear regression fit … many options
  2. Plot is well organised - The independent (explanatory) variable is on the x and the dependent (respnse) variable is on the y axis
  3. X and Y axes use correct units - Having proper symbols (for alpha, beta, etc.) and super/subscript where needed
  4. X and Y axes easy to read - Beware awkward fonts and tiny letters
  5. Clear informative legend - It’s easy to tell apart what points/lines on the graph represent
  6. Plot is not cluttered - Don’t put all results on one plot, give them space to shine
  7. Clear and consistent colour scheme - Stick with the same colours for the same variables, avoid red/green combinations which might look the same to colourblind people
  8. Plot is the right dimensions - Avoid overlapping labels and points/lines which merge together and make your graph longer/wider if needed
  9. Measures of uncertainty where appropriate - Error bars, confidence and credible intervals, remember to say in the caption what they are
  10. Concise and informative caption - Remember to include what the data points show (raw data? Model predictions?), what is the sample size for each treatment, the effect size and what measure of uncertainty accompanies it

21 Summary

Visualizing data is one of the most important activities in applied statistics & in science.

There is a large number of good (and bad) practices -> you can quickly see whether a certain graphic is effective in conveying its message

Important options:

  • plot type (what is called a geom in ggplot2)
  • proportions (incl. aspect ratios)
  • colors.

The grammar of graphics is a powerful set of concepts to reason about graphics and to communicate our intentions for a data visualization to a computer.

Creating your own visualizations is in many ways like good writing. It is extremely important, but there is no simple recipe for it.

Look carefully at lots of visualizations made by others & experiment with making your own visualizations to learn the ropes

Yes, we just scratched the surface! Data viz is a scientific discipline in its own


Session information

We report the version numbers of R and all the packages used in this session.

Why?

It is good practice to always keep such a record of this! By including this at the bottom of a script, your reports will become more reproducible.

sessionInfo()
# R version 4.2.0 (2022-04-22)
# Platform: x86_64-apple-darwin17.0 (64-bit)
# Running under: macOS Big Sur/Monterey 10.16
# 
# Matrix products: default
# BLAS:   /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRblas.0.dylib
# LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib
# 
# locale:
# [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
# 
# attached base packages:
# [1] stats4    stats     graphics  grDevices utils     datasets  methods  
# [8] base     
# 
# other attached packages:
#  [1] pheatmap_1.0.12      plotly_4.10.0        ggforce_0.3.3       
#  [4] ggbeeswarm_0.6.0     tidyr_1.2.0          dplyr_1.0.9         
#  [7] ggplot2_3.3.6        Hiiragi2013_1.32.0   xtable_1.8-4        
# [10] RColorBrewer_1.1-3   mouse4302.db_3.13.0  org.Mm.eg.db_3.15.0 
# [13] MASS_7.3-57          KEGGREST_1.36.0      gtools_3.9.2        
# [16] gplots_3.1.3         geneplotter_1.74.0   annotate_1.74.0     
# [19] XML_3.99-0.9         AnnotationDbi_1.58.0 IRanges_2.30.0      
# [22] S4Vectors_0.34.0     lattice_0.20-45      genefilter_1.78.0   
# [25] cluster_2.1.3        clue_0.3-60          boot_1.3-28         
# [28] affy_1.74.0          Biobase_2.56.0       BiocGenerics_0.42.0 
# 
# loaded via a namespace (and not attached):
#  [1] colorspace_2.0-3       ellipsis_0.3.2         XVector_0.36.0        
#  [4] rstudioapi_0.13        farver_2.1.0           hexbin_1.28.2         
#  [7] affyio_1.66.0          bit64_4.0.5            fansi_1.0.3           
# [10] xml2_1.3.3             splines_4.2.0          cachem_1.0.6          
# [13] knitr_1.39             polyclip_1.10-0        jsonlite_1.8.0        
# [16] png_0.1-7              icons_0.2.0            BiocManager_1.30.17   
# [19] compiler_4.2.0         httr_1.4.3             assertthat_0.2.1      
# [22] Matrix_1.4-1           fastmap_1.1.0          lazyeval_0.2.2        
# [25] cli_3.3.0              tweenr_1.0.2           htmltools_0.5.2       
# [28] tools_4.2.0            gtable_0.3.0           glue_1.6.2            
# [31] GenomeInfoDbData_1.2.8 rappdirs_0.3.3         Rcpp_1.0.8.3          
# [34] jquerylib_0.1.4        vctrs_0.4.1            Biostrings_2.64.0     
# [37] preprocessCore_1.58.0  crosstalk_1.2.0        xfun_0.31             
# [40] stringr_1.4.0          lifecycle_1.0.1        zlibbioc_1.42.0       
# [43] scales_1.2.0           yaml_2.3.5             memoise_2.0.1         
# [46] sass_0.4.1             latticeExtra_0.6-29    stringi_1.7.6         
# [49] RSQLite_2.2.14         highr_0.9              caTools_1.18.2        
# [52] GenomeInfoDb_1.32.2    rlang_1.0.2            pkgconfig_2.0.3       
# [55] bitops_1.0-7           evaluate_0.15          purrr_0.3.4           
# [58] htmlwidgets_1.5.4      labeling_0.4.2         bit_4.0.4             
# [61] tidyselect_1.1.2       magrittr_2.0.3         bookdown_0.26         
# [64] R6_2.5.1               generics_0.1.2         DBI_1.1.2             
# [67] pillar_1.7.0           withr_2.5.0            survival_3.3-1        
# [70] RCurl_1.98-1.6         tibble_3.1.7           crayon_1.5.1          
# [73] KernSmooth_2.23-20     utf8_1.2.2             rmarkdown_2.14        
# [76] jpeg_0.1-9             grid_4.2.0             isoband_0.2.5         
# [79] data.table_1.14.2      blob_1.2.3             digest_0.6.29         
# [82] munsell_0.5.0          beeswarm_0.4.0         viridisLite_0.4.0     
# [85] vipor_0.4.5            bslib_0.3.1
LS0tCnRpdGxlOiA+CiAgRGF0YSB2aXogLSBoYW5kcy1vbiBzZXNzaW9uCnN1YnRpdGxlOiA+CiAgR1RJUEkgU3VtbWVyU2Nob29sCiAgPHAgYWxpZ249ImNlbnRlciI+CiAgPGEgaHJlZj0iaHR0cHM6Ly9pbWJlaW1haW56LmdpdGh1Yi5pby9HVElQSTIwMjIiPjxpbWcgc3JjPSJpbWFnZXMvZ3RpcGlfbG9nby5wbmciIGFsdD0iIiBoZWlnaHQ9IjE1MCIvPjwvYT4KICA8L3A+CmF1dGhvcjoKLSBuYW1lOiA8YSBocmVmPSJodHRwczovL2ZlZGVyaWNvbWFyaW5pLmdpdGh1Yi5pbyI+RmVkZXJpY28gTWFyaW5pIChtYXJpbmlmQHVuaS1tYWluei5kZSk8L2E+PGJyPjxhIGhyZWY9Imh0dHBzOi8vd3d3LnVuaW1lZGl6aW4tbWFpbnouZGUvaW1iZWkvIj5JTUJFSSwgVW5pdmVyc2l0eSBNZWRpY2FsIENlbnRlciBNYWluejwvYT48YnI+PGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9GZWRlQmlvaW5mbyI+YHIgaWNvbnM6OmZvbnRhd2Vzb21lKCd0d2l0dGVyJylgIGBARmVkZUJpb2luZm9gPC9hPgpkYXRlOiAiMjAyMi8wNi8wMiIKb3V0cHV0OiAKICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBjb2xsYXBzZSA9IFRSVUUsCiAgY29tbWVudCA9ICIjIiwKICBlcnJvciA9IEZBTFNFLAogIHdhcm5pbmcgPSBGQUxTRSwKICBtZXNzYWdlID0gRkFMU0UKKQpgYGAKCgoKIyBCYWNrIHRvIHRoZSBiYXNlIC0gYHBsb3RgCgpXZSdyZSBnb2luZyB0byB1c2UgdGhlIGBETmFzZWAgZGF0YXNldCwgaW5jbHVkZWQgaW4gUidzIGRpc3RyaWJ1dGlvbgoKWW91IG1pZ2h0IHdhbnQgdG8gcmVhZCB1cCBhIGxpdHRsZSBvbiBpdCwgc28gdHlwZSBgaGVscChETmFzZSlgIHRvIGRvIHNvIC0geWVzLCBuaWNlIGRhdGFzZXRzIFNIT1VMRCBiZSBkb2N1bWVudGVkCgpgYGB7cn0KaGVhZChETmFzZSkKcGxvdChETmFzZSRjb25jLCBETmFzZSRkZW5zaXR5KQpgYGAKCi0tLQoKIyBCYWNrIHRvIHRoZSBiYXNlIC0gYHBsb3RgCgpDdXN0b21pemluZyB0aGlzIGEgbGl0dGxlLi4uCgpFLmcuIGNvbG9yIHRoZSBkb3RzIGluIGJsdWUsIGFuZCBhZGQgdmVydGljYWwgbGluZXMgZm9yIGVhY2ggdmFsdWUgb2YgY29uY2VudHJhdGlvbgoKPGRldGFpbHM+CgpgYGB7cn0KcGxvdChETmFzZSRjb25jLCBETmFzZSRkZW5zaXR5LAogIHlsYWIgPSBhdHRyKEROYXNlLCAibGFiZWxzIikkeSwKICB4bGFiID0gcGFzdGUoYXR0cihETmFzZSwgImxhYmVscyIpJHgsIGF0dHIoRE5hc2UsICJ1bml0cyIpJHgpLAogIHBjaCA9IDMsCiAgY29sID0gImJsdWUiKQphYmxpbmUodiA9IHVuaXF1ZShETmFzZSRjb25jKSwgbHR5ID0gImRvdHRlZCIpCmBgYAoKPC9kZXRhaWxzPgoKLS0tCgojIEJhY2sgdG8gdGhlIGJhc2UgLSBgcGxvdGAKCkNvbG9yIHRoZSBwb2ludHMgYnkgdGhlaXIgdmFsdWUgaW4gdGhlIFJ1biBjb2x1bW4KCjxkZXRhaWxzPgoKYGBge3J9CnBsb3QoRE5hc2UkY29uLCBETmFzZSRkZW5zaXR5LCBjb2wgPSBETmFzZSRSdW4pCmBgYAoKPC9kZXRhaWxzPgoKCgotLS0KCiMgQmFjayB0byB0aGUgYmFzZSAtIGBwbG90YAoKV2hhdCBpZiB5b3UgbmVlZCB0byBjaGVjayB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgdmFyaWFibGU/CgpXaGF0IGlmIHlvdSB3YW50ZWQgYSBib3hwbG90PwoKQ2FuIHlvdSBjaGVjayBvdXQgaG93IHRvIHNwbGl0IHRoZSBib3hwbG90cyBieSB0aGVpciBSdW4/Cgo8ZGV0YWlscz4KCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NX0KcGFyKG1mcm93ID0gYygxLCAyKSkKaGlzdChETmFzZSRkZW5zaXR5KSAKYm94cGxvdChkZW5zaXR5IH4gUnVuLCBETmFzZSkKYGBgCgo8L2RldGFpbHM+CgotLS0KCiMgV2hhdCdzICJ3cm9uZyIgd2l0aCB0aGlzPwoKV2hpbGUgaXQgaXMgZWZmZWN0aXZlIHRvIHF1aWNrbHkgcHJvZHVjZSBvdXQgb2YgdGhlIGJveCBmaWd1cmVzLi4uCgotIHRoZXJlIGlzIG5vdCBnbG9iYWwgb3ZlcnZpZXcgYW5kIHBhcmFtZXRlcml6YXRpb24gb2YgdGhlIHZpc3VhbGl6YXRpb24KLSB0aGUgbGF5b3V0IGRlY2lzaW9ucyBoYXZlIHRvIGJlIG1hZGUgdXAgdXBmcm9udAotIGV2ZXJ5IGFzcGVjdCBvZiB0aGUgZmlndXJlIGlzIGN1c3RvbWlzZWQgbG9jYWxseSBhcyBmdW5jdGlvbiBhcmd1bWVudHMgKG9uZSBjb3VsZCBhcmd1ZSB0aGF0IHRoaXMgaXMgYSBwbHVzKQotIHRoZXJlIGlzIG5vIHVuaWZpZWQgdHlwZSBvZiBkYXRhIGFjcm9zcyBhbGwgZnVuY3Rpb25zIHdoaWNoIG1ha2VzIGl0IGVmZmljaWVudCBmb3Igc29tZSB0eXBlcyBvZiBkYXRhIChpZiB0aGV5IG1hdGNoKSwgYnV0IGFsc28gdmVyeSBoZXRlcm9nZW5lb3VzIGluIHRlcm1zIG9mIGludGVyZmFjZSAtIGJhc2UgZ3JhcGhpY3MgZnVuY3Rpb25zIHdpbGwgd29yayB3aXRoIHZhcmlvdXMgaW5wdXRzOiBhIGRhdGEuZnJhbWUsIHZlY3RvcnMsIGEgZm9ybXVsYSwuLi4KLSBkZWZhdWx0cyAoY29sb3VycyBpbiBwYXJ0aWN1bGFyISkgYXJlIHBvb3JseSBjaG9zZW4KCldlJ2xsIHNvb24gdXNlIGEgdmlzdWFsaXphdGlvbiBmcmFtZXdvcmsgY2FsbGVkIHRoZSAqZ3JhbW1hciBvZiBncmFwaGljcyogKGFzIGluIGBnZ3Bsb3QyYCkgLSBlbmFibGluZyBzdGVwIGJ5IHN0ZXAgY29uc3RydWN0aW9uIG9mIGhpZ2ggcXVhbGl0eSBncmFwaGljcyBpbiBhIGxvZ2ljYWwgYW5kIGVsZWdhbnQgbWFubmVyLiAKCkJ1dCBmaXJzdDogaGktZGltIGRhdGEgdGltZSEKCi0tLQoKIyBgSGlpcmFnaTIwMTNgCgpBIGdlbmUgZXhwcmVzc2lvbiBtaWNyb2FycmF5IGRhdGFzZXQgdGhhdCByZXBvcnRzIHRoZSB0cmFuc2NyaXB0b21lcyBvZiBhcm91bmQgMTAwIGluZGl2aWR1YWwgY2VsbHMgZnJvbSBtb3VzZSBlbWJyeW9zIGF0IGRpZmZlcmVudCB0aW1lIHBvaW50cyBpbiBlYXJseSBkZXZlbG9wbWVudAoKYGBge3J9CmxpYnJhcnkoIkhpaXJhZ2kyMDEzIikKZGF0YSgieCIpCmRpbShCaW9iYXNlOjpleHBycyh4KSkKCmhlYWQocERhdGEoeCksIG4gPSAyKQpgYGAKCldlJ2xsIGRvIHNvbWUgd3JhbmdsaW5nIG9uIHRoYXQgc29vbiwgc28gbGV0J3MgY3JlYXRlIAoKYGBge3J9CmRmdHggPC0gZGF0YS5mcmFtZSh0KEJpb2Jhc2U6OmV4cHJzKHgpKSwgcERhdGEoeCkpCmBgYAoKLS0tCgojIGBnZ3Bsb3QyYDogdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MKClRoZSBjb21wb25lbnRzIG9mIGBnZ3Bsb3QyYCdzIGdyYW1tYXIgb2YgZ3JhcGhpY3MgYXJlCgoxLiBvbmUgb3IgbW9yZSBkYXRhc2V0cywKMS4gb25lIG9yIG1vcmUgZ2VvbWV0cmljIG9iamVjdHMgdGhhdCBzZXJ2ZSBhcyB0aGUgdmlzdWFsIHJlcHJlc2VudGF0aW9ucyBvZiB0aGUgZGF0YSwg4oCTIGZvciBpbnN0YW5jZSwgcG9pbnRzLCBsaW5lcywgcmVjdGFuZ2xlcywgY29udG91cnMsCjEuIGRlc2NyaXB0aW9ucyBvZiBob3cgdGhlIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBhcmUgbWFwcGVkIHRvIHZpc3VhbCBwcm9wZXJ0aWVzIChhZXN0aGV0aWNzKSBvZiB0aGUgZ2VvbWV0cmljIG9iamVjdHMsIGFuZCBhbiBhc3NvY2lhdGVkIHNjYWxlIChlLiBnLiwgbGluZWFyLCBsb2dhcml0aG1pYywgcmFuayksCjEuIG9uZSBvciBtb3JlIGNvb3JkaW5hdGUgc3lzdGVtcywKMS4gc3RhdGlzdGljYWwgc3VtbWFyaXphdGlvbiBydWxlcywKMS4gYSBmYWNldCBzcGVjaWZpY2F0aW9uLCBpLmUuIHRoZSB1c2Ugb2YgbXVsdGlwbGUgc2ltaWxhciBzdWJwbG90cyB0byBsb29rIGF0IHN1YnNldHMgb2YgdGhlIHNhbWUgZGF0YSwKMS4gb3B0aW9uYWwgcGFyYW1ldGVycyB0aGF0IGFmZmVjdCB0aGUgbGF5b3V0IGFuZCByZW5kZXJpbmcsIHN1Y2ggdGV4dCBzaXplLCBmb250IGFuZCBhbGlnbm1lbnQsIGxlZ2VuZCBwb3NpdGlvbnMuCgpTaW1wbGUgZm9ybToKCmBgYApnZ3Bsb3QoZGF0YSA9IDxEQVRBPiwgbWFwcGluZyA9IGFlcyg8TUFQUElOR1M+KSkgKyAgPEdFT01fRlVOQ1RJT04+KCkKYGBgCgotLS0KCiMgQWR2YW50YWdlcyBvZiB0aGUgYGdncGxvdDJgIGZyYW1ld29yawoKLSBUaGUgZWFzZSBvZiBnZXR0aW5nIGEgZ29vZCBsb29raW5nIHBsb3QKLSBFYXN5IGN1c3RvbWl6YXRpb24KLSBBIGxvdCBvZiBuZWNlc3NhcnkgZGF0YSBwcm9jZXNzaW5nIGlzIGRvbmUgZm9yIHlvdQotIENsZWFyIHN5bnRheAotIEVhc3kgbXVsdGlkaW1lbnNpb25hbCBhcHByb2FjaAotIERlY2VudCBkZWZhdWx0IGNvbG9yIHNjaGVtZSBhcyBhIGRlZmF1bHQKLSBMb3RzIG9mIGV4dGVuc2lvbnMKCi0tLQoKIyBSZS1kb2luZyB0aGUgRE5hc2UgcGxvdC4uLgoKRXhlcmNpc2U6IGNyZWF0ZSBhIHNpbWlsYXIgcGxvdCBvZiBETmFzZSBkZW5zaXR5IHZzIGNvbmNlbnRyYXRpb24sIGxpa2Ugd2UganVzdCBkaWQgZm9yIGJhc2UgZ3JhcGhpY3MuCgo8ZGV0YWlscz4KCmBgYHtyfQpsaWJyYXJ5KCJnZ3Bsb3QyIikKZ2dwbG90KCkKZ2dwbG90KGRhdGEgPSBETmFzZSkKZ2dwbG90KGRhdGEgPSBETmFzZSwKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGNvbmMsIHkgPSBkZW5zaXR5KSkKCmdncGxvdChkYXRhID0gRE5hc2UsCiAgICAgICBtYXBwaW5nID0gYWVzKHggPSBjb25jLCB5ID0gZGVuc2l0eSkpICsgCiAgZ2VvbV9wb2ludCgpCnAgPC0gZ2dwbG90KGRhdGEgPSBETmFzZSwKICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gY29uYywgeSA9IGRlbnNpdHkpKSArIAogIGdlb21fcG9pbnQoKQpwCnAgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFJ1bikpCnAgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFzLmNoYXJhY3RlcihSdW4pKSkKIyBnZ3NhdmUoIkROQXNlLWhpc3RvZ3JhbS1kZW1vLnBkZiIsIHBsb3QgPSBwKSAjIGNoZWNrIG91dCB0aGUgZG9jcyBmb3IgdGhpcyEKYGBgCgo8L2RldGFpbHM+CgpXaGljaCBvbmUgaXMgeW91ciBmYXZvcml0ZT8gV2h5PwoKPCEtLSBkZWZpbmUgYSBtYXBwaW5nICh1c2luZyB0aGUgYWVzdGhldGljIChhZXMpIGZ1bmN0aW9uKSwgYnkgc2VsZWN0aW5nIHRoZSB2YXJpYWJsZXMgdG8gYmUgcGxvdHRlZCBhbmQgc3BlY2lmeWluZyBob3cgdG8gcHJlc2VudCB0aGVtIGluIHRoZSBncmFwaCwgZS5nLiBhcyB4L3kgcG9zaXRpb25zIG9yIGNoYXJhY3RlcmlzdGljcyBzdWNoIGFzIHNpemUsIHNoYXBlLCBjb2xvdXIsIGV0Yy4gLS0+CgotLS0KCiMgRXhwbG9yaW5nIGBIaWlyYWdpMjAxM2AKCldoYXQgYXJlIHdlIGRvaW5nIGhlcmU/CgpgYGB7ciBldmFsPUZBTFNFfQpnZ3Bsb3QoZGZ0eCwgYWVzKHggPSBYMTQyNjY0Ml9hdCwgeSA9IFgxNDE4NzY1X2F0KSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIikKYGBgCgotLQoKYW5kIGhlcmU/CgpgYGB7ciBldmFsPUZBTFNFfQpnZ3Bsb3QoZGZ0eCwgYWVzKHggPSBYMTQyNjY0Ml9hdCwgeSA9IFgxNDE4NzY1X2F0KSkgICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHNhbXBsZUNvbG91ciksIHNoYXBlID0gMTkpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiKSArCiAgc2NhbGVfY29sb3JfZGlzY3JldGUoKQpgYGAKCi0tLQoKIyBWaXN1YWxpemluZyAxRCBkYXRhCgpBIGNvbW1vbiB0YXNrIGluIGJpb2xvZ2ljYWwgZGF0YSBhbmFseXNpcyBpcyB0aGUgY29tcGFyaXNvbiBiZXR3ZWVuIHNldmVyYWwgc2FtcGxlcyBvZiB1bml2YXJpYXRlIG1lYXN1cmVtZW50cy4gSW4gdGhpcyBzZWN0aW9uIHdl4oCZbGwgZXhwbG9yZSBzb21lIHBvc3NpYmlsaXRpZXMgZm9yIHZpc3VhbGl6aW5nIGFuZCBjb21wYXJpbmcgc3VjaCBzYW1wbGVzLiBBcyBhbiBleGFtcGxlLCB3ZeKAmWxsIHVzZSB0aGUgaW50ZW5zaXRpZXMgb2YgYSBzZXQgb2YgZm91ciBnZW5lczogRmdmNCwgR2F0YTQsIEdhdGE2IGFuZCBTb3gyCgpgYGB7cn0Kc2VsZWN0ZWRQcm9iZXMgPSBjKEZnZjQgPSAiMTQyMDA4NV9hdCIsIEdhdGE0ID0gIjE0MTg4NjNfYXQiLAogICAgICAgICAgICAgICAgICAgR2F0YTYgPSAiMTQyNTQ2M19hdCIsIFNveDIgPSAiMTQxNjk2N19hdCIpCmxpYnJhcnkoImRwbHlyIikKbGlicmFyeSgidGlkeXIiKQp0bXAgPC0gZGF0YS5mcmFtZSh0KGV4cHJzKHhbc2VsZWN0ZWRQcm9iZXMsIF0pKSkKbmFtZXModG1wKSA8LSBuYW1lcyhzZWxlY3RlZFByb2JlcykKdG1wJHNhbXBsZSA8LSByb3duYW1lcyh0bXApCmhlYWQodG1wKQpnZW5lcyA8LSBnYXRoZXIodG1wLCBrZXkgPSAiZ2VuZSIsIHZhbHVlID0gImV4cHJlc3Npb24iLCAtc2FtcGxlKQpoZWFkKGdlbmVzKQpgYGAKClRoaXMgYGdlbmVzYCBkYXRhLmZyYW1lIGlzIGluIHRoZSBzby1jYWxsZWQgdGlkeSBmb3JtYXQhICAKYGdncGxvdDJgIExPVkVTIHRpZHkgZGF0YQoKLS0tCgojIDFEOiBiYXJwbG90cywgYm94cGxvdHMsIGRvdCwgaml0dGVyLCB2aW9saW5zCgpgYGB7cn0KZ2dwbG90KGdlbmVzLCBhZXMoeCA9IGdlbmUsIHkgPSBleHByZXNzaW9uKSkgKwogIHN0YXRfc3VtbWFyeShmdW4gPSBtZWFuLCBnZW9tID0gImJhciIpCmBgYAoKT3V0IG9mIHRoZSBmb2xsb3dpbmcgcmVwcmVzZW50YXRpb25zLCBsZXQncyBkaXNjdXNzIHdoaWNoIG9uZSB5b3UgcHJlZmVyIG1vc3QvbGVhc3QKCmBgYHtyfQpwIDwtIGdncGxvdChnZW5lcywgYWVzKCB4ID0gZ2VuZSwgeSA9IGV4cHJlc3Npb24sIGZpbGwgPSBnZW5lKSkKcCArIGdlb21fYm94cGxvdCgpCmBgYAoKVHJ5IG5vdyBzb21lIG1vcmUgb3B0aW9ucyBvbiB0aGlzIG9iamVjdCBgcGAuIFRyeSB0byBhZGQgaml0dGVyZWQgcG9pbnRzLCBvciB1c2UgYSB2aW9saW4gcGxvdAoKPGRldGFpbHM+CgpgYGB7cn0KcCArIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBnZW5lKSkKcCArIGdlb21fdmlvbGluKCkKcCArIGdlb21fZG90cGxvdChiaW5heGlzID0gInkiLCBiaW53aWR0aCA9IDEvNiwKICAgICAgIHN0YWNrZGlyID0gImNlbnRlciIsIHN0YWNrcmF0aW8gPSAwLjc1LAogICAgICAgYWVzKGNvbG9yID0gZ2VuZSkpCmxpYnJhcnkoImdnYmVlc3dhcm0iKQpwICsgZ2VvbV9iZWVzd2FybShhZXMoY29sb3IgPSBnZW5lKSkKbGlicmFyeSgiZ2dmb3JjZSIpCnAgKyBnZW9tX3NpbmEoYWVzKGNvbG9yID0gZ2VuZSkpCmBgYAoKPC9kZXRhaWxzPgoKWW91IGNhbiBldmVuIHN0YWNrIG11bHRpcGxlIGBnZW9tX2BzIG9uIGFub3RoZXIhCgojIDFEOiBkZW5zaXRpZXMsIGhpc3RvZ3JhbXMKCmBgYHtyfQpnZW5lcyAlPiUKICBmaWx0ZXIoZ2VuZSA9PSAiR2F0YTQiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBleHByZXNzaW9uKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKYGBge3J9CmdncGxvdChnZW5lcywgYWVzKHggPSBleHByZXNzaW9uLCBjb2xvciA9IGdlbmUpKSArIAogIGdlb21fZGVuc2l0eSgpCgpnZ3Bsb3QoZ2VuZXMsIGFlcyh4ID0gZXhwcmVzc2lvbiwgY29sb3IgPSBnZW5lKSkgKyAKICBnZW9tX2RlbnNpdHkoKSArIAogIHRoZW1lX2J3KCkKCmBgYAoKVGhlcmUgYXJlIHNvb29vIG1hbnkgdGhlbWVzIGF2YWlsYWJsZSAtIEkgbGlrZSBjbGVhbiBvbmVzLCBidXQgb2Z0ZW4gaXQgZGVwZW5kcyBvbiB5b3VyIHB1cnBvc2UhCgotLS0KCiMgVmlzdWFsaXppbmcgMkQgZGF0YQoKYGBge3J9CmRmeCA8LSBhcy5kYXRhLmZyYW1lKEJpb2Jhc2U6OmV4cHJzKHgpKQpzY3AgPC0gZ2dwbG90KGRmeCwgYWVzKHg9IGA1OSBFNC41IChQRSlgLCAKICAgICAgICAgICAgICAgICAgICAgICB5ID0gYDkyIEU0LjUgKEZHRjQtS08pYCkpCnNjcCArIGdlb21fcG9pbnQoKQpgYGAKCgpDYW4geW91IHRoaW5rIG9mIGEgd2F5IHRvIHJlZHVjZSB0aGUgb3ZlcnBsb3R0aW5nIGhlcmU/Cgo8ZGV0YWlscz4KCmBgYHtyfQpzY3AgKyBnZW9tX3BvaW50KGFscGhhID0gMC4zKQpzY3AgKyBnZW9tX2RlbnNpdHkyZChoID0gMC41LCBiaW5zID0gNjApCnNjcCArIGdlb21faGV4KCkgKyBjb29yZF9maXhlZCgpCmBgYAoKPC9kZXRhaWxzPgoKLS0tCgojIFZpc3VhbGl6aW5nIGRhdGEgYWxvbmcgbW9yZSBkaW1lbnNpb25zCgpXaGVuIHZpc3VhbGlzaW5nIGRhdGEgYWxvbmcgYWRkaXRpb25hbCBkaW1lbnNpb24sIHdlIGNhbiBwYXJhbWV0ZXJpemUgdGhlIHBvaW50cyBieSBzZXR0aW5nIHRoZWlyIHNoYXBlLCBjb2xvdXIsIHNpemUgYW5kIHRyYW5zcGFyZW5jeSwgdGhhdCBjYW4gYmUgc2V0IHdpdGggcG9pbnQgYWVzdGhldGljcyBzdWNoIGFzIGZpbGwsIGNvbG9yIChvciBjb2xvdXIpLCBzaGFwZSwgc2l6ZSBhbmQgYWxwaGEuCgpBIHZlcnkgcG93ZXJmdWwgd2F5IHRvIHJlcHJlc2VudCBkYXRhIGFsb25nIGFkZGl0aW9uYWwgZGltZW5zaW9ucyBpcyBmYWNldHRpbmcsIGkuZS4gcHJvZHVjaW5nIHN1Yi1wbG90cyBmb3IgZGlmZmVyZW50IHN1YnNldHMgb2YgdGhlIGRhdGEuIEJlbG93LCB3ZSBmaXJzdCByZS1hbm5vdGF0ZSB0aGUgZGF0YSB1c2luZyBzb21lIHJlZ3VsYXIgZXhwcmVzc2lvbnMKCmBgYHtyfQpnZ3Bsb3QoZGZ0eCwgYWVzKHggPSBYMTQyNjY0Ml9hdCwgeSA9IFgxNDE4NzY1X2F0LCBjb2xvdXIgPSBsaW5lYWdlKSkgKwogIGdlb21fcG9pbnQoKQpnZ3Bsb3QoZGZ0eCwgYWVzKHggPSBYMTQyNjY0Ml9hdCwgeSA9IFgxNDE4NzY1X2F0KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfZ3JpZCggLiB+IGxpbmVhZ2UgKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGZ0eCwKICAgICAgIGFlcyh4ID0gWDE0MjY2NDJfYXQsIHkgPSBYMTQxODc2NV9hdCkpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X2dyaWQoIEVtYnJ5b25pYy5kYXkgfiBsaW5lYWdlICkKYGBgCgpZb3VyIHR1cm46IFVzZSBmYWNldHMgdG8gdmlzdWFsaXNlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGZvdXIgRmdmNCwgR2F0YTQsIEdhdGE2IGFuZCBTb3gyIGdlbmVzIGluIHRoZSBgZ2VuZXNgIGRhdGEgdXNpbmcgaGlzdG9ncmFtcy4KCjxkZXRhaWxzPgoKYGBge3J9CmdncGxvdChnZW5lcywgYWVzKHggPSBleHByZXNzaW9uKSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIGZhY2V0X3dyYXAofiBnZW5lKQpgYGAKCjwvZGV0YWlscz4KCi0tLQoKIyBJbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9ucwoKYGBge3J9CnAyIDwtIHAgKyBnZW9tX2ppdHRlcihhZXMoY29sb3VyID0gZ2VuZSkpCmxpYnJhcnkoInBsb3RseSIpCmdncGxvdGx5KHAyKQpgYGAKClNvbWV0aW1lcyB0aGlzIGlzIGFsbCB5b3UgbWlnaHQgbmVlZCEKCi0tLQoKIyBBbiBhcHBldGl6ZXIgZm9yIFJOQS1zZXE/CgouLi5vciBtYW55IG90aGVyIGhpZ2gtZGltZW5zaW9uYWwgZGF0YQoKKiBNQSBwbG90Ciogdm9sY2FubyBwbG90CiogaGVhdG1hcHMKKiBQQ0EgcGxvdAoqIHRTTkUgcGxvdAoqIG90aGVyIGdlbm9taWMgZGF0YSAoa2FyeW9wbG90cywgLi4uKQoKV2hhdCBkbyB0aGVzZSBwbG90cyBkbz8gTGV0J3MgZGlzY3VzcyB0b2dldGhlci4KCi0tLQoKIyBBbiBhcHBldGl6ZXIgZm9yIFJOQS1zZXE/CgpgYGB7cn0KbGlicmFyeSgicGhlYXRtYXAiKQpsaWJyYXJ5KCJkcGx5ciIpCmdyb3VwcyA8LSBncm91cF9ieShwRGF0YSh4KSwgc2FtcGxlR3JvdXApICU+JQogIHN1bW1hcmlzZShuID0gbigpLCBjb2xvciA9IHVuaXF1ZShzYW1wbGVDb2xvdXIpKQpncm91cENvbG9yIDwtIHNldE5hbWVzKGdyb3VwcyRjb2xvciwgZ3JvdXBzJHNhbXBsZUdyb3VwKQp0b3BHZW5lcyA8LSBvcmRlcihyb3dWYXJzKEJpb2Jhc2U6OmV4cHJzKHgpKSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6NTAwXQpyb3dDZW50ZXIgPC0gZnVuY3Rpb24oeCkgeyB4IC0gcm93TWVhbnMoeCkgfQpwaGVhdG1hcCggcm93Q2VudGVyKEJpb2Jhc2U6OmV4cHJzKHgpWyB0b3BHZW5lcywgXSApLAogIHNob3dfcm93bmFtZXMgPSBGQUxTRSwgc2hvd19jb2xuYW1lcyA9IEZBTFNFLAogIGJyZWFrcyA9IHNlcSgtNSwgKzUsIGxlbmd0aCA9IDEwMSksCiAgYW5ub3RhdGlvbl9jb2wgPQogICAgcERhdGEoeClbLCBjKCJzYW1wbGVHcm91cCIsICJnZW5vdHlwZSIsICJFbWJyeW9uaWMuZGF5IiwgIlNjYW5EYXRlIikgXSwKICBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoCiAgICBzYW1wbGVHcm91cCA9IGdyb3VwQ29sb3IsCiAgICBnZW5vdHlwZSA9IGMoYEZHRjQtS09gID0gImNob2NvbGF0ZTEiLCBgV1RgID0gImF6dXJlMiIpLAogICAgRW1icnlvbmljLmRheSA9IHNldE5hbWVzKGJyZXdlci5wYWwoOSwgIkJsdWVzIilbYygzLCA2LCA5KV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiRTMuMjUiLCAiRTMuNSIsICJFNC41IikpLAogICAgU2NhbkRhdGUgPSBzZXROYW1lcyhicmV3ZXIucGFsKG5sZXZlbHMoeCRTY2FuRGF0ZSksICJZbEduIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyh4JFNjYW5EYXRlKSkKICApLAogIGN1dHJlZV9yb3dzID0gNAopCmBgYAoKCi0tLQoKIyBHb3QgYSBuaWNlIHZpej8KCkxldCdzIHRyeSB0byBkaXNzZWN0IHRoYXQKCgotLS0KCiMgQSBjaGVja2xpc3QKCjEuIEFwcHJvcHJpYXRlIHBsb3QgdHlwZSBmb3IgcmVzdWx0cyAtIE1pZ2h0IGJlIGEgYm94cGxvdCwgYSBzY2F0dGVycGxvdCwgYSBsaW5lYXIgcmVncmVzc2lvbiBmaXQgLi4uIG1hbnkgb3B0aW9ucwoyLiBQbG90IGlzIHdlbGwgb3JnYW5pc2VkIC0JVGhlIGluZGVwZW5kZW50IChleHBsYW5hdG9yeSkgdmFyaWFibGUgaXMgb24gdGhlIHggYW5kIHRoZSBkZXBlbmRlbnQgKHJlc3Buc2UpIHZhcmlhYmxlIGlzIG9uIHRoZSB5IGF4aXMKMy4gWCBhbmQgWSBheGVzIHVzZSBjb3JyZWN0IHVuaXRzIC0gSGF2aW5nIHByb3BlciBzeW1ib2xzIChmb3IgYWxwaGEsIGJldGEsIGV0Yy4pIGFuZCBzdXBlci9zdWJzY3JpcHQgd2hlcmUgbmVlZGVkCjQuIFggYW5kIFkgYXhlcyBlYXN5IHRvIHJlYWQgLSBCZXdhcmUgYXdrd2FyZCBmb250cyBhbmQgdGlueSBsZXR0ZXJzCjUuIENsZWFyIGluZm9ybWF0aXZlIGxlZ2VuZCAtIEl0J3MgZWFzeSB0byB0ZWxsIGFwYXJ0IHdoYXQgcG9pbnRzL2xpbmVzIG9uIHRoZSBncmFwaCByZXByZXNlbnQKNi4gUGxvdCBpcyBub3QgY2x1dHRlcmVkIC0gRG9uJ3QgcHV0IGFsbCByZXN1bHRzIG9uIG9uZSBwbG90LCBnaXZlIHRoZW0gc3BhY2UgdG8gc2hpbmUKNy4gQ2xlYXIgYW5kIGNvbnNpc3RlbnQgY29sb3VyIHNjaGVtZSAtIFN0aWNrIHdpdGggdGhlIHNhbWUgY29sb3VycyBmb3IgdGhlIHNhbWUgdmFyaWFibGVzLCBhdm9pZCByZWQvZ3JlZW4gY29tYmluYXRpb25zIHdoaWNoIG1pZ2h0IGxvb2sgdGhlIHNhbWUgdG8gY29sb3VyYmxpbmQgcGVvcGxlCjguIFBsb3QgaXMgdGhlIHJpZ2h0IGRpbWVuc2lvbnMgLSBBdm9pZCBvdmVybGFwcGluZyBsYWJlbHMgYW5kIHBvaW50cy9saW5lcyB3aGljaCBtZXJnZSB0b2dldGhlciBhbmQgbWFrZSB5b3VyIGdyYXBoIGxvbmdlci93aWRlciBpZiBuZWVkZWQKOS4gTWVhc3VyZXMgb2YgdW5jZXJ0YWludHkgd2hlcmUgYXBwcm9wcmlhdGUgLSBFcnJvciBiYXJzLCBjb25maWRlbmNlIGFuZCBjcmVkaWJsZSBpbnRlcnZhbHMsIHJlbWVtYmVyIHRvIHNheSBpbiB0aGUgY2FwdGlvbiB3aGF0IHRoZXkgYXJlCjEwLiBDb25jaXNlIGFuZCBpbmZvcm1hdGl2ZSBjYXB0aW9uIC0gUmVtZW1iZXIgdG8gaW5jbHVkZSB3aGF0IHRoZSBkYXRhIHBvaW50cyBzaG93IChyYXcgZGF0YT8gTW9kZWwgcHJlZGljdGlvbnM/KSwgd2hhdCBpcyB0aGUgc2FtcGxlIHNpemUgZm9yIGVhY2ggdHJlYXRtZW50LCB0aGUgZWZmZWN0IHNpemUgYW5kIHdoYXQgbWVhc3VyZSBvZiB1bmNlcnRhaW50eSBhY2NvbXBhbmllcyBpdAoKPCEtLSAtLS0gLS0+Cgo8IS0tICMgQ2hvb3NpbmcgdGhlIHJpZ2h0IHZpc3VhbGl6YXRpb24gc29mdHdhcmUgLS0+Cgo8IS0tIFJlcHJvZHVjaWJpbGl0eSBhbmQgcmVwZWF0YWJpbGl0eSAtLT4KPCEtLSBEYXRhIGV4cGxvcmF0aW9uIHZlcnN1cyBkYXRhIHByZXNlbnRhdGlvbiAtLT4KPCEtLSBTZXBhcmF0aW9uIG9mIGNvbnRlbnQgYW5kIGRlc2lnbiAtLT4KCjwhLS0gLS0tIC0tPgoKPCEtLSAjIFRlbGxpbmcgYSBzdG9yeSBhbmQgbWFraW5nIGEgcG9pbnQgLS0+Cgo8IS0tIE1vc3QgZGF0YSB2aXN1YWxpemF0aW9uIGlzIGRvbmUgZm9yIHRoZSBwdXJwb3NlIG9mIGNvbW11bmljYXRpb24uIFdlIGhhdmUgYW4gaW5zaWdodCBhYm91dCBhIGRhdGFzZXQsIGFuZCB3ZSBoYXZlIGEgcG90ZW50aWFsIGF1ZGllbmNlLCBhbmQgd2Ugd291bGQgbGlrZSB0byBjb252ZXkgb3VyIGluc2lnaHQgdG8gb3VyIGF1ZGllbmNlLiBUbyBjb21tdW5pY2F0ZSBvdXIgaW5zaWdodCBzdWNjZXNzZnVsbHksIHdlIHdpbGwgaGF2ZSB0byBwcmVzZW50IHRoZSBhdWRpZW5jZSB3aXRoIGEgY2xlYXIgYW5kIGV4Y2l0aW5nIHN0b3J5LiBUaGUgbmVlZCBmb3IgYSBzdG9yeSBtYXkgc2VlbSBkaXN0dXJiaW5nIHRvIHNjaWVudGlzdHMgYW5kIGVuZ2luZWVycywgd2hvIG1heSBlcXVhdGUgaXQgd2l0aCBtYWtpbmcgdGhpbmdzIHVwLCBwdXR0aW5nIGEgc3BpbiBvbiB0aGluZ3MsIG9yIG92ZXJzZWxsaW5nIHJlc3VsdHMuIEhvd2V2ZXIsIHRoaXMgcGVyc3BlY3RpdmUgbWlzc2VzIHRoZSBpbXBvcnRhbnQgcm9sZSB0aGF0IHN0b3JpZXMgcGxheSBpbiByZWFzb25pbmcgYW5kIG1lbW9yeS4gLS0+Cgo8IS0tIFdoYXQgaXMgYSBzdG9yeT8gLS0+Cgo8IS0tIEJlZm9yZSB3ZSBjYW4gZGlzY3VzcyBzdHJhdGVnaWVzIGZvciB0dXJuaW5nIHZpc3VhbGl6YXRpb25zIGludG8gc3Rvcmllcywgd2UgbmVlZCB0byB1bmRlcnN0YW5kIHdoYXQgYSBzdG9yeSBhY3R1YWxseSBpcy4gQSBzdG9yeSBpcyBhIHNldCBvZiBvYnNlcnZhdGlvbnMsIGZhY3RzLCBvciBldmVudHMsIHRydWUgb3IgaW52ZW50ZWQsIHRoYXQgYXJlIHByZXNlbnRlZCBpbiBhIHNwZWNpZmljIG9yZGVyIHN1Y2ggdGhhdCB0aGV5IGNyZWF0ZSBhbiBlbW90aW9uYWwgcmVhY3Rpb24gaW4gdGhlIGF1ZGllbmNlLiBUaGUgZW1vdGlvbmFsIHJlYWN0aW9uIGlzIGNyZWF0ZWQgdGhyb3VnaCB0aGUgYnVpbGQtdXAgb2YgdGVuc2lvbiBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBzdG9yeSBmb2xsb3dlZCBieSBzb21lIHR5cGUgb2YgcmVzb2x1dGlvbiB0b3dhcmRzIHRoZSBlbmQgb2YgdGhlIHN0b3J5LiBXZSByZWZlciB0byB0aGUgZmxvdyBmcm9tIHRlbnNpb24gdG8gcmVzb2x1dGlvbiBhbHNvIGFzIHRoZSBzdG9yeSBhcmMsIGFuZCBldmVyeSBnb29kIHN0b3J5IGhhcyBhIGNsZWFyLCBpZGVudGlmaWFibGUgYXJjLiAtLT4KCjwhLS0gTmV2ZXIgYXNzdW1lIHlvdXIgYXVkaWVuY2UgY2FuIHJhcGlkbHkgcHJvY2VzcyBjb21wbGV4IHZpc3VhbCBkaXNwbGF5cy4gIC0tPgoKPCEtLSBkbyBldmVyeXRoaW5nIHdlIGNhbiB0byBoZWxwIG91ciByZWFkZXJzIHVuZGVyc3RhbmQgdGhlIG1lYW5pbmcgb2Ygb3VyIHZpc3VhbGl6YXRpb25zIGFuZCBzZWUgdGhlIHNhbWUgcGF0dGVybnMgaW4gdGhlIGRhdGEgdGhhdCB3ZSBzZWUuIFRoaXMgdXN1YWxseSBtZWFucyBsZXNzIGlzIG1vcmUuIFNpbXBsaWZ5IHlvdXIgZmlndXJlcyBhcyBtdWNoIGFzIHBvc3NpYmxlLiAtLT4KCgotLS0KCiMgU3VtbWFyeQoKVmlzdWFsaXppbmcgZGF0YSBpcyBvbmUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IGFjdGl2aXRpZXMgaW4gYXBwbGllZCBzdGF0aXN0aWNzICYgaW4gc2NpZW5jZS4gCgpUaGVyZSBpcyBhIGxhcmdlIG51bWJlciBvZiBnb29kIChhbmQgYmFkKSBwcmFjdGljZXMgLT4geW91IGNhbiBxdWlja2x5IHNlZSB3aGV0aGVyIGEgY2VydGFpbiBncmFwaGljIGlzIGVmZmVjdGl2ZSBpbiBjb252ZXlpbmcgaXRzIG1lc3NhZ2UKCkltcG9ydGFudCBvcHRpb25zOiAKCi0gcGxvdCB0eXBlICh3aGF0IGlzIGNhbGxlZCBhIGdlb20gaW4gZ2dwbG90MikKLSBwcm9wb3J0aW9ucyAoaW5jbC4gYXNwZWN0IHJhdGlvcykgCi0gY29sb3JzLiAKClRoZSBncmFtbWFyIG9mIGdyYXBoaWNzIGlzIGEgcG93ZXJmdWwgc2V0IG9mIGNvbmNlcHRzIHRvIHJlYXNvbiBhYm91dCBncmFwaGljcyBhbmQgdG8gY29tbXVuaWNhdGUgb3VyIGludGVudGlvbnMgZm9yIGEgZGF0YSB2aXN1YWxpemF0aW9uIHRvIGEgY29tcHV0ZXIuCgpDcmVhdGluZyB5b3VyIG93biB2aXN1YWxpemF0aW9ucyBpcyBpbiBtYW55IHdheXMgbGlrZSBnb29kIHdyaXRpbmcuIEl0IGlzIGV4dHJlbWVseSBpbXBvcnRhbnQsIGJ1dCB0aGVyZSBpcyBubyBzaW1wbGUgcmVjaXBlIGZvciBpdC4gCgpMb29rIGNhcmVmdWxseSBhdCBsb3RzIG9mIHZpc3VhbGl6YXRpb25zIG1hZGUgYnkgb3RoZXJzICYgZXhwZXJpbWVudCB3aXRoIG1ha2luZyB5b3VyIG93biB2aXN1YWxpemF0aW9ucyB0byBsZWFybiB0aGUgcm9wZXMKCipZZXMsIHdlIGp1c3Qgc2NyYXRjaGVkIHRoZSBzdXJmYWNlISogRGF0YSB2aXogaXMgYSBzY2llbnRpZmljIGRpc2NpcGxpbmUgaW4gaXRzIG93bgoKLS0tCgojIFNlc3Npb24gaW5mb3JtYXRpb24gey19CgpXZSByZXBvcnQgdGhlIHZlcnNpb24gbnVtYmVycyBvZiBSIGFuZCBhbGwgdGhlIHBhY2thZ2VzIHVzZWQgaW4gdGhpcyBzZXNzaW9uLiAKCldoeT8KCkl0IGlzIGdvb2QgcHJhY3RpY2UgdG8gYWx3YXlzIGtlZXAgc3VjaCBhIHJlY29yZCBvZiB0aGlzIQpCeSBpbmNsdWRpbmcgdGhpcyBhdCB0aGUgYm90dG9tIG9mIGEgc2NyaXB0LCB5b3VyIHJlcG9ydHMgd2lsbCBiZWNvbWUgbW9yZSByZXByb2R1Y2libGUuCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==